/*
 * WorkerSpace.cpp
 *
 *  Created on: 28-10-2011
 *      Author: cristian
 */

#include "WorkerSpace.h"

/*
 *	Constructor del WorkerSpace
 *	@Param WorkerSpace* padre Si es NULL indica que es un Workerspace Servidor
 *	(No tiene espacio padre)
 */
WorkerSpace::WorkerSpace(WorkerSpace* root) :
		Ambiente<WorkerSpace>() {
	this->setRun(false);
	this->setRoot(root);

//	servers = new list<string>;
//	servers->push_back("10.1.1.1");
//	servers->push_back("10.1.1.2");

	logger = new Logger();
	setMaxThreads();
}

WorkerSpace::~WorkerSpace() {

	HAL_TRACE_CORE(logger, " Destructor del Workspace id:%i size:%i ", id, getSize());

	stopWorker();
	clearAmbiente();

	delete servers;
	delete logger;
	closeSock();
}

std::string WorkerSpace::getDescripcion() const {
	return descripcion;
}

unsigned int WorkerSpace::getId() const {
	return id;
}

std::string WorkerSpace::getNombre() const {
	return nombre;
}

void WorkerSpace::setDescripcion(std::string descripcion) {
	this->descripcion = descripcion;
}

void WorkerSpace::setId(unsigned int id) {
	this->id = id;
}

void WorkerSpace::setNombre(std::string nombre) {
	this->nombre = nombre;
}

void WorkerSpace::clearAmbiente() {

	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator it;
	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator end = this->getAmbiente2()->end();

	for (it = this->getAmbiente2()->begin(); it != end; ++it) {

		map<unsigned int, WorkerSpace*>* valuey = it->second;
		if (valuey != NULL) {

			map<unsigned int, WorkerSpace*>::const_iterator it2;
			map<unsigned int, WorkerSpace*>::const_iterator end2 = valuey->end();

			for (it2 = valuey->begin(); it2 != end2; ++it2) {
				WorkerSpace* worker = it2->second;
				if (worker != NULL && typeid(worker) == typeid(WorkerSpace*)) {
					delete ((WorkerSpace*) worker);
				}
			}

			valuey->clear();
			delete valuey;
		}
	}
	this->getAmbiente2()->clear();
}

void* WorkerSpace::start(void *object) {
	((WorkerSpace*) object)->work(object);
	return NULL;
}

WorkerSpace *WorkerSpace::getRoot() const {
	return root;
}

void WorkerSpace::setRoot(WorkerSpace *_root) {

	//TODO Los workerspace se deben crear dentro del workerspace.

	if (_root == NULL) {
//
//		try {
//			this->server(PUERTO);
//		} catch (WorkerSpaceException e) {
//			HAL_ERROR(logger, "%s", e.what());
//		}
//		this->setIsServer(true);

		return;
	}

	this->root = _root;
}

void WorkerSpace::work(void *object) {

	this->setRun(true);

	while (isRun()) {

		if (this->isIsServer()) {

			HAL_TRACE_SERVER(logger, " listening..server");
			this->listening();

			HAL_TRACE_CLIENT(logger, " Conexion Aceptada ....");

		} else {

			int i = 0;

			while (1) {

				i++;

				sleep(5);

				 struct hostent *he;

				 if ((he=gethostbyname("www.android-spa.com"))==NULL) {
				       printf("error de gethostbyname()\n");
				       exit(-1);
				    }

				    printf("Nombre del host: %s\n",he->h_name);
				       /* muestra el nombre del nodo */
//				    printf("Direccion IP: %s\n",);
//				       /* muestra la direcci�n IP */

				string* protocol = new string("http://");
				string* host = new string("www.android-spa.com");
				string* get = new string("/rss.xml");
				string* ip = new string( inet_ntoa(*((struct in_addr *)he->h_addr)));


				HttpHeader* http = new HttpHeader(get,host);

				string* statusserver = this->sendData("188.165.222.14", 80, http->toString());

				if (statusserver != NULL) {
					HAL_TRACE_CLIENT(logger, "%s", statusserver->c_str());
				}

				delete host;
				delete get;
				delete http;
				delete statusserver;
				delete protocol;

			}
		}
	}
}

unsigned int WorkerSpace::getSize() const {

	int totalsize = 0;
	;

	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator it;
	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator end = this->getAmbiente2()->end();

	for (it = this->getAmbiente2()->begin(); it != end; ++it) {

		map<unsigned int, WorkerSpace*>* valuey = it->second;

		if (valuey != NULL)
			totalsize += valuey->size();
	}

	return totalsize;
}

void WorkerSpace::addWorkerSpace(WorkerSpace *worker) {
	if (worker != NULL) {
		worker->setRoot(this);
		srand (time(NULL) );unsigned
		int x = rand() % size;
		unsigned int y = rand() % size;
		this->addWorkerSpace(x, y, worker);
	}
}

void WorkerSpace::addWorkerSpace(unsigned int x, unsigned int y, WorkerSpace *worker) {

	if (worker != NULL) {
		//worker->setRoot(this);
	}

	this->setNodo(x, y, worker);
	this->size++;

}

void WorkerSpace::stopWorker(unsigned int x, unsigned int y) {
	WorkerSpace* nodo = this->getNodo(x, y);
	if (nodo != NULL) {
		nodo->stopWorker();
	}
}

void WorkerSpace::stopWorker(unsigned int id) {

}

void WorkerSpace::stopWorker() {

	this->setRun(false);
	this->stopWorkers();

//	HAL_TRACE_CORE(logger, "Parando Worker %i", id);
	//pthread_cancel(*(this)->getThread());

}

unsigned int WorkerSpace::getMaxThreads() const {
	return max_threads;
}

void WorkerSpace::setMaxThreads() {
	int numCPU = sysconf(_SC_NPROCESSORS_ONLN);
	int max = numCPU * MAX_THREAD_MUL;
	this->setMaxThreads(max);
}

void WorkerSpace::stopWorkers() {

	if (this->getSize() > 0) {

		map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator it;
		map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator end = this->getAmbiente2()->end();

		for (it = this->getAmbiente2()->begin(); it != end; ++it) {

			map<unsigned int, WorkerSpace*>* valuey = it->second;

			map<unsigned int, WorkerSpace*>::const_iterator it2;
			map<unsigned int, WorkerSpace*>::const_iterator end2 = valuey->end();

			for (it2 = valuey->begin(); it2 != end2; ++it2) {
				WorkerSpace* worker = it2->second;
				if (worker != NULL && typeid(worker) == typeid(WorkerSpace*)) {

					if (((WorkerSpace*) worker)->isRun()) {
						((WorkerSpace*) worker)->stopWorker();
						//	HAL_TRACE_CORE(logger, " stop worker %i \n", ((WorkerSpace*) worker)->getId());
						int i = pthread_detach((*((WorkerSpace*) worker)->getThread()));

						if (i <= 0) {
							puts(strerror(errno));
							HAL_TRACE_CORE(logger, " Detach pthread %s \n", strerror(errno));
						}
					}
				}
			}
		}
	}
}

void WorkerSpace::setMaxThreads(unsigned int max_threads) {
	this->max_threads = max_threads;
}

void WorkerSpace::runAllWorkers() {

	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator it;
	map<unsigned int, map<unsigned int, WorkerSpace*>*>::const_iterator end = this->getAmbiente2()->end();

	for (it = this->getAmbiente2()->begin(); it != end; ++it) {

		map<unsigned int, WorkerSpace*>* valuey = it->second;

		map<unsigned int, WorkerSpace*>::const_iterator it2;
		map<unsigned int, WorkerSpace*>::const_iterator end2 = valuey->end();

		for (it2 = valuey->begin(); it2 != end2; ++it2) {
			WorkerSpace* worker = it2->second;
			if (worker != NULL && typeid(worker) == typeid(WorkerSpace*)) {
				int rc = pthread_create(worker->getThread(), worker->getAttr(), WorkerSpace::start, worker);
				if (rc) {
					puts(strerror(errno));
					HAL_ERROR(logger, "No se puede crear hilo worker :( : %s", strerror(errno));
				}
			}
		}
	}
}

void WorkerSpace::startWorker(unsigned int x, unsigned int y) {
	if (x < 0) {
		//TODO Error
		return;
	}
	if (y < 0) {
		//TODO Error
		return;
	}

	WorkerSpace* nodo = this->getNodo(x, y);

	int rc = pthread_create(nodo->getThread(), nodo->getAttr(), WorkerSpace::start, nodo);
	if (rc) {

	}
}

void WorkerSpace::stopWorker(WorkerSpace *workerspace) {
	workerspace->stopWorker();
	pthread_join(*workerspace->getThread(), NULL);
}

bool WorkerSpace::moveMe(WorkerSpace *workerspace, unsigned int x2, unsigned int y2) {
   pthread_mutex_lock(&mutex);
	if (this->getId() == workerspace->getRoot()->getId()) {

	}

	if (workerspace == NULL) {
		//TODO ERROR
		return false;
	}

	WorkerSpace* nodo = this->getNodo(x2, y2);
	if (nodo != NULL) {

		return false;

	}

// TODO ELiminado de mmeoria del nodo NULL
	this->addWorkerSpace(x2, y2, workerspace);
	 pthread_mutex_unlock(&mutex);
	return true;
}

void WorkerSpace::close() {
	//1 Parar los hilos
	//2 borrar los Workers

	stopWorker();

}

void WorkerSpace::listening() {

	int i = 0;
	while (1) {

		HalSocket* sock = new HalSocket;

		try {
			this->accept(*sock);

		} catch (WorkerSpaceException e) {
			HAL_ERROR(logger, "%s", e.what());
		}

		this->getIp(sock);

		std::string* text = new string;

		try {
			*sock >> *text;
		} catch (WorkerSpaceException e) {
			HAL_ERROR(logger, "%s", e.what());
		}

		HAL_TRACE_SERVER(logger, "peticion recibida desde el cliente : %s", text->c_str());

		delete text;

		string * xmlstatus = XmlUtils::statusServerXML(getNombre(), getId(), getSize(), getIp(sock));

		srand(time(NULL));

		try {
			*sock << *xmlstatus;
		} catch (WorkerSpaceException e) {
			HAL_ERROR(logger, "%s", e.what());
		}

		delete sock;
		delete xmlstatus;
		i++;

		HAL_TRACE_SERVER(logger, "llamado de cliente  numero %i ip %s", i,getIp(sock)->c_str());

	}
}

string *WorkerSpace::createXmlStatus() {
	string* xml = NULL;
	return xml;
}

string* WorkerSpace::sendData(string host, int port, string *data) {

	//HAL_TRACE_CORE(logger, "send ... request .. %s", data->c_str());
	try {
		this->client(host, port);
	} catch (WorkerSpaceException e) {
		HAL_ERROR(logger, "%s", e.what());
		return NULL;
	}

	try {
		(*this) << *data;
	} catch (WorkerSpaceException e) {
		HAL_ERROR(logger, "%s", e.what());
		return NULL;
	}

	string* serverstatus = new string();

	try {
		(*this) >> *serverstatus;
	} catch (WorkerSpaceException e) {
		HAL_ERROR(logger, "%s", e.what());
		return NULL;
	}

	closeSock();
	return serverstatus;

}

